#define __LIBRARY__

#include <stdio.h>
#include <sem.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

_syscall2(sem_t*,sem_open,const char*,name,unsigned int,value);
_syscall1(int,sem_wait,sem_t*,sem);
_syscall1(int,sem_post,sem_t*,sem);
_syscall1(int,sem_unlink,const char*,name);

#define name_size 32
#define BUFSIZE 10
#define CONSUMER_NUM 4
#define MAX_NUM 500

sem_t *empty, *full, *mutex;

int read_place;
int fd_rp;
int fd; 

void producer() {
	int write_place = 0;

	int item_num = 0;

	while(item_num <= MAX_NUM) {
		
		sem_wait(empty);           

		sem_wait(mutex);          

		lseek(fd, write_place, SEEK_SET);
		write(fd, &item_num, sizeof(int));
		write_place = lseek(fd, 0, SEEK_CUR); 
		sync(); 
		item_num++;
		sem_post(mutex); 

		sem_post(full); 
	}	
}

void consumer() {
	int item_num;
	do{
		
	    sem_wait(full); 
	    sem_wait(mutex); 
	    			
		lseek(fd_rp, 0, SEEK_SET);
		read(fd_rp, &read_place, sizeof(int));
		
		if(read_place == -1) {
			sem_post(mutex);
			break;
		}

		lseek(fd, read_place, SEEK_SET); 
		read(fd, &item_num, sizeof(int));

		read_place = (item_num == MAX_NUM) ? -1 : lseek(fd, 0, SEEK_CUR);
		lseek(fd_rp, 0, SEEK_SET);
		write(fd_rp, &read_place, sizeof(int));
		sync();

		printf("%d: %d\n", getpid(), item_num);
		fflush(stdout);

		sem_post(mutex); 

		sem_post(empty);
		
	}while(item_num < MAX_NUM);
	
	sem_post(full);
}

int main() {

	char empty_name[name_size];
	char full_name[name_size];
	char mutex_name[name_size];
	
	int i;

	sprintf(empty_name, "/%ld_empty", (long)getpid());    
    	sprintf(full_name, "/%ld_full", (long)getpid());
    	sprintf(mutex_name, "/%ld_mutex", (long)getpid());

	empty = sem_open(empty_name, BUFSIZE);
	full = sem_open(full_name, 0);
	mutex = sem_open(mutex_name, 1);

	fd = open("share.file", O_CREAT | O_RDWR, 0666); 

	if(!fork()) {
		producer();
		exit(0);
	}
	
	fd_rp = open("read_place.txt", O_CREAT | O_RDWR, 0666);
	
	for(i = 0; i < CONSUMER_NUM; i++) {
		if(i == 0) {
			read_place = 0;
			lseek(fd_rp, 0, SEEK_SET);
			write(fd_rp, &read_place, sizeof(int));
		}
		if(!fork()) {
			consumer();
			exit(0);
		}
	}
	
	while(wait(NULL) != -1);
	
	sem_unlink(empty_name);
	sem_unlink(full_name);
	sem_unlink(mutex_name);

	close(fd);
	close(fd_rp);
	return 0;	
}
